/* -*- c++ -*- */
/*
 *   Copyright 2011,2012 Erik Persson
 *
 *   This file is part of the cell-sync-usrp project.
 *
 *   cell-sync-usrp is free software: you can redistribute it and/or modify
 *   it under the terms of the GNU General Public License as published by
 *   the Free Software Foundation, either version 3 of the License, or
 *   (at your option) any later version.
 *
 *   cell-sync-usrp is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU General Public License for more details.
 *
 *   You should have received a copy of the GNU General Public License
 *   along with cell-sync-usrp.  If not, see <http://www.gnu.org/licenses/>.
 */


#ifndef INCLUDED_WCDMA_SLOT_SYNC_CVC_H
#define INCLUDED_WCDMA_SLOT_SYNC_CVC_H

#include <gr_sync_block.h>

#include <vector>

class wcdma_slot_sync_cvc;

#define PCCPCH_FRAME_BUFFER_SIZE 32

/*
 * We use boost::shared_ptr's instead of raw pointers for all access
 * to gr_blocks (and many other data structures).  The shared_ptr gets
 * us transparent reference counting, which greatly simplifies storage
 * management issues.  This is especially helpful in our hybrid
 * C++ / Python system.
 *
 * See http://www.boost.org/libs/smart_ptr/smart_ptr.htm
 *
 * As a convention, the _sptr suffix indicates a boost::shared_ptr
 */
typedef boost::shared_ptr<wcdma_slot_sync_cvc> wcdma_slot_sync_cvc_sptr;

class wcdma_slot_sync_cvc;

typedef int (wcdma_slot_sync_cvc::*state_function_t)(int,
		  gr_vector_int &,
		  gr_vector_const_void_star &,
		  gr_vector_void_star &);

struct find_frame_sync_state_data {
	int internal_slot_number;
	int current_sync_code;
	int times;
	int samples_elapsed;
	bool frame_sync_read;
	int loop_for_num_samples;	// specifies number of samples to loop
	bool do_loop;				// perform the loop
	float accumulation[15][16];
	float match[16];
	unsigned char ssc_codes[15];
	gr_complex ssc[512];
	unsigned char scrambling_group;
};

#define SCRAMBLING_SEQ_BUFF_LEN 36
struct in_sync_state_data {
	int frame_cntr;							// should wrap each 1000 frame
	int frame_sample_index;					// index of what sample within the current frame
	float psc_accumulation[11];				// accumulation buffer for performing psc synch
	gr_complex psc_sum[11];					// used to calculate the sum for the correlation
	unsigned char scrambling_group;			// the scrambling group 0-63
	unsigned char scrambling_code_number;	// the primary scrambling code number
	unsigned char xy_scrambling_buffer[SCRAMBLING_SEQ_BUFF_LEN*2][4];		// used for online calculation of the scrambling sequence
	int detected_psc_code;					// the detected primary scrambling Code
	gr_complex last_CPICH_symbol;			// the last symbol received on the CPICH
	gr_complex last_scrambling_chip_Z;		// the last chip read from the scrambling sequence
	gr_complex last_chip;					// the last chip read from the rx chain
	gr_complex last_descrambled_chip;		// the last descrambled chip
	gr_complex last_rotated_CPICH_symbol;	// last rotated CPICH symbol
	gr_complex last_PCCPCH_symbol;			// last saved PCCPCH symbol
	gr_complex last_rotated_PCCPCH_symbol;	// last rotated PCCPCH symbol
	float CPICHRotMatrix[4];				// rotation matrix used to phase rotate symbols
    unsigned char PCCPCH_last_frame[270];	// the bits from the last frame on the PCCPCH
    int *done;								// should point to the d_done in wcdma_slot_sync_cvc
    unsigned int PCCPCH_frame_buffer_counter;   // used to index what PCCPCH frame buffer to store data to
    std::vector<unsigned char> *PCCPCH_frame_buffer;  // should point to the PCCPCH_frame_buffer in wcdma_slot_sync_cvc
};

/*!
 * \brief Return a shared_ptr to a new instance of wcdma_slot_sync_cvc.
 *
 * To avoid accidental use of raw pointers, wcdma_slot_sync_cvc's
 * constructor is private.  wcdma_make_slot_sync_cvc is the public
 * interface for creating new instances.
 */
wcdma_slot_sync_cvc_sptr wcdma_make_slot_sync_cvc ();

/*!
 * \brief square2 a stream of floats.
 * \ingroup block
 *
 * This uses the preferred technique: subclassing gr_sync_block.
 */
class wcdma_slot_sync_cvc : public gr_block
{
private:
  // The friend declaration allows wcdma_make_slot_sync_cvc to
  // access the private constructor.

  friend wcdma_slot_sync_cvc_sptr wcdma_make_slot_sync_cvc ();

  wcdma_slot_sync_cvc ();  	// private constructor

  std::vector<unsigned char> *d_PCCPCH_frame_buffer;	// used to store the first PCCPCH_FRAME_BUFFER_SIZE received PCCPCH frames.
  int d_done;								    // when done decoding  > 0 or failed for some reason < 0
  int d_number_of_retries;					// number of retries done for finding sync

  std::vector<gr_complex> *d_corr_buffer;	// should contain the last received sequence of samples
  std::vector<float> *d_acc_buffer;			// buffer used to accumulate the result
  int d_corr_buff_pos;						// the current position in the d_corr_buffer
  int d_acc_buff_pos;						// the current position in the d_acc_buffer
  int d_acc_buff_hit_index;					// index of where in the accumulation buffer the latest detected peak appeared.
  int d_acc_buff_wrap_cntr;					// counts the number of wraps that occured when iterating over the accumulation buffer

  int d_state;								// internal state
  int d_internal_slot_number;				// internal numbering of slots

  unsigned char *d_state_data;							// state data

  state_function_t *d_state_functions;	// the state functions should be allocated in this array, and should map to the STATE_ defines

  int state_wait_on_radio(int noutput_items,
			  gr_vector_int &ninput_items,
			  gr_vector_const_void_star &input_items,
			  gr_vector_void_star &output_items);

  int state_find_slot_sync(int noutput_items,
		  gr_vector_int &ninput_items,
		  gr_vector_const_void_star &input_items,
		  gr_vector_void_star &output_items);
  int state_find_frame_sync(int noutput_items,
		  gr_vector_int &ninput_items,
		  gr_vector_const_void_star &input_items,
		  gr_vector_void_star &output_items);
  int state_in_sync(int noutput_items,
		  gr_vector_int &ninput_items,
		  gr_vector_const_void_star &input_items,
		  gr_vector_void_star &output_items);

  void setState(int state, void *param);					// sets the current state
  void print_buffers();						// prints the buffers
  int find_acc_hit_index();					// finds the index in the d_acc_buffer with the highest value
  //float correlate();
  float correlate(int code_number);		// performs correlation with the code passed as a parameter
  float correlate(gr_complex *buffer, int code_number);		// performs correlation with the code passed as a parameter
  void copy_last_512_samples(gr_complex *buffer);	// copies the last 512 samples to the buffer specified by buffer
  int find_slot_match(find_frame_sync_state_data *data,int internal_slot_number);

  unsigned short check_scrambling_code_group(unsigned char group_number, unsigned char *codes);	// same as check_scrambling_code_groups but only for the specified group
  unsigned short check_scrambling_code_groups(unsigned char *codes); // returns the scrambling group in the high 8 bits and the slot frame offset in the lower 8 bits returns -1 on error

 public:
  ~wcdma_slot_sync_cvc ();	// public destructor

  // Where all the action really happens

  unsigned char getPCCPCHData(int frame, int i);
  int isDone();
  int getNumberOfRetries();

  int general_work (int noutput_items,
		  gr_vector_int &ninput_items,
		  gr_vector_const_void_star &input_items,
		  gr_vector_void_star &output_items);
};

#endif /* INCLUDED_WCDMA_SLOT_SYNC_CVC_H */
